﻿using System;
using System.Collections.Generic;
using System.Diagnostics.Contracts;

namespace OpenCube.Nbt.Mapping
{
	/// <summary>
	/// Allows conversion from NBT values to more strongly typed Model values.
	/// </summary>
	[ContractClass(typeof(NbtTypeConverterContract))]
	public interface INbtTypeConverter
	{
		/// <summary>
		/// Asks the converter if it can convert the given model.
		/// </summary>
		/// <param name="sourceModel">The model type to convert from.</param>
		/// <returns><c>true</c> when it can convert, otherwise <c>false.</c></returns>
		bool CanConvertToNbt(Type sourceModel);

		/// <summary>
		/// Asks the converter if it can convert the given nbt type(s) to the target model.
		/// </summary>
		/// <param name="sourceNbtTypes">The source NBT type(s) of the values to convert from.</param>
		/// <param name="targetModelType">The model type to convert to</param>
		/// <returns><c>true</c> when it can convert, otherwise <c>false.</c></returns>
		bool CanConvertToModel(IReadOnlyList<Type> sourceNbtTypes, Type targetModelType);

		/// <summary>
		/// Convert from the NBT value to the Model value
		/// </summary>
		/// <param name="conversionContext">The conversion context to get optional data you might need.</param>
		/// <param name="nbtValue">The NBT value to convert.</param>
		/// <returns>The Model value matching the NBT value.</returns>
		object ConvertToModelValue(IConversionContext conversionContext, IReadOnlyList<object> nbtValue);

		/// <summary>
		/// Convert from the Model value to the NBT value.
		/// </summary>
		/// <param name="conversionContext">The conversion context to get optional data you might need.</param>
		/// <param name="modelValue">The Model value to convert.</param>
		/// <returns>The NBT value matcing the Model value.</returns>
		IEnumerable<object> ConvertToNbtValue(IConversionContext conversionContext, object modelValue);
	}

	[ContractClassFor(typeof(INbtTypeConverter))]
	abstract class NbtTypeConverterContract
		: INbtTypeConverter
	{
		public bool CanConvertToNbt(Type sourceModel)
		{
			Contract.Requires<ArgumentNullException>(sourceModel != null);
			throw new NotImplementedException();
		}

		public bool CanConvertToModel(IReadOnlyList<Type> sourceNbtTypes, Type targetModelType)
		{
			Contract.Requires<ArgumentNullException>(sourceNbtTypes != null);
			Contract.Requires<ArgumentNullException>(targetModelType != null);
			throw new NotImplementedException();
		}

		public object ConvertToModelValue(IConversionContext conversionContext, IReadOnlyList<object> nbtValue)
		{
			Contract.Requires<ArgumentNullException>(conversionContext != null);
			Contract.Requires<ArgumentNullException>(nbtValue != null);
			throw new NotImplementedException();
		}

		public IEnumerable<object> ConvertToNbtValue(IConversionContext conversionContext, object modelValue)
		{
			Contract.Requires<ArgumentNullException>(conversionContext != null);
			Contract.Ensures(Contract.Result<IEnumerable<object>>() != null);
			throw new NotImplementedException();
		}
	}
}
